/*
* Work with files
* Author: Nikolay A. Makarov
*/
#include ".\datastore2.h"

void datastore2::displayResults( vector< vector<char*> > result)
{

	cout<<"\n------------------------------------------------------------------------------------\n";

	for (int pos = 0; pos<result.size(); pos++) {
		cout<<"|  "<<result[pos][0]<<"  |";
	}
	cout<<"\n------------------------------------------------------------------------------------\n";

	bool flag;
	/*
	col_name1 val11 ... ... ...
	col_name2 val21 ... ... ...
	col_name3 val31 ... ... ...
	*/
	/*test print NORMAL RESULT*/
	/*for ( int i = 0; i < result.size(); i++)
	{
	for ( int j = 0; j < result[0].size(); j++)
	{
	if ( result[i][j]!=NULL)
	{
	cout<<"||\t"<<result[i][j]<<"\t";
	flag = true;

	}
	else
	{
	flag = false;
	}

	}
	if (flag) cout<<"\n";
	}
	*/
	//FORMAT OUTPUT
	for ( int i = 1; i < result[0].size(); i++)
	{
		for ( int j = 0; j < result.size(); j ++)
		{
			if ( result[j][i] != NULL && strcmp(result[j][i], "(null)") != 0) {
				cout<<"|  "<<result[j][i]<<"  |";
				flag = true;
			}
			else
			{
				flag = false;
			}
		}
		if (flag) cout<<"\n";
	}	
	cout<<"------------------------------------------------------------------------\n";

}
vector< vector<char*> > datastore2::getDataFromTable(char* nameOfTable)
{
	std::string fileAdress(nameOfTable);
	fileAdress = fileAdress + "\\";
	fileAdress = fileAdress + nameOfTable;
	if ( (stream2 = fopen( (char*) fileAdress.data(), "r")) == NULL) {
		dbmsException e;
		std::string sStart("Can`t open table. File \"");
		std::string sFileName(nameOfTable);
		std::string sEnd("\" doesn`t exist.");
		std::string errorMessage;
		errorMessage = sStart + sFileName;
		errorMessage = errorMessage + sEnd;
		e.setMessage(errorMessage);
		throw e;
	} else {
		vector< vector<char*> > result;
		char* type;
		char* nameOfColumn;
		type = (char*) malloc(sizeof(char)*100);
		while ( fscanf(stream2,"%s",type)!=EOF)
		{

			nameOfColumn = (char*) malloc (sizeof(char)*100);
			fscanf(stream2,"%s",nameOfColumn);
			vector<char *> data = this->getDataFromColumn(nameOfColumn, nameOfTable);
			result.push_back(data);
			data.clear();
			type = (char*) malloc(sizeof(char)*100);
		}

		fclose(stream2);
		//Push columns types to vector
		vector< vector<char*> > types;
		types = getColumns(nameOfTable);
		if(types.size() == result.size()) {
			for(int i = 0; i < result.size(); i++) {
				result[i].push_back(types[i].back());
			}
			
		} else {
			dbmsException e;
			e.setMessage("dbcoreError: types size <> result size");
			throw e;
		}
			
		if (relationalModel::isTableRelational(result, nameOfTable)) {
			return result;
		} else {
			//If table isn`t relational throw exception
			dbmsException e;
			std::string sStart("Table \" ");
			std::string sFileName(nameOfTable);
			std::string sEnd("\" isn`t relational table.");
			std::string errorMessage;
			errorMessage = sStart + sFileName;
			errorMessage = errorMessage + sEnd;
			e.setMessage(errorMessage);
			throw e;
		}
	}

}

vector<char*> datastore2::getTables()
{
	vector<char*> tables;
		long hFile;
		struct _finddata_t c_file;
		if( (hFile = _findfirst( "*.*", &c_file )) == -1L ) {
			dbmsException e;
			e.setMessage("Database haven`t some tables..");
			throw e;
		} else {
	
		TCHAR folderPath1[MAX_PATH];
		GetCurrentDirectory(sizeof(folderPath1),folderPath1);
		strcat(folderPath1, "\\");
		strcat(folderPath1, c_file.name);
		
		DWORD dwAttr = GetFileAttributes(strcat(folderPath1, c_file.name));	
		if(dwAttr & FILE_ATTRIBUTE_DIRECTORY) {
			if(strcmp(c_file.name, "..")!=0 && strcmp(c_file.name, ".")!=0 && strcmp(c_file.name, ".svn")!=0 && strcmp(c_file.name, "Debug")!=0) {
				if(dwAttr & FILE_ATTRIBUTE_ARCHIVE) {
					char* tableName;
					tableName = (char*) malloc(sizeof(char)*100);
					strcpy(tableName, c_file.name);
					tables.push_back(tableName);
					
				}
			}
		}

		while( _findnext( hFile, &c_file ) == 0 )
		{
			TCHAR folderPath[MAX_PATH];
			char* tableName; 
			tableName = (char*) malloc(sizeof(char)*100);
			GetCurrentDirectory(sizeof(folderPath),folderPath);
			strcat(folderPath, "\\");
			strcat(folderPath, c_file.name);
			DWORD dwAttr = GetFileAttributes(folderPath);	
			if(dwAttr & FILE_ATTRIBUTE_DIRECTORY) {
			if(strcmp(c_file.name, "..")!=0 && strcmp(c_file.name, ".")!=0 && strcmp(c_file.name, ".svn")!=0 && strcmp(c_file.name, "Debug")!=0) {
				strcpy(tableName, c_file.name);	
				tables.push_back(tableName);
				
				}
			}
	
		}
		
		_findclose( hFile );
	}
	return tables;
}

vector< vector<char*> > datastore2::getColumns(char *nameOfTable) {
	vector< vector<char*> > result;
	std::string fileAdress(nameOfTable);
	fileAdress = fileAdress + "\\";
	fileAdress = fileAdress + nameOfTable;
	if ( (stream2 = fopen( (char*) fileAdress.data(), "r")) == NULL) {
		dbmsException e;
		std::string sStart("Can`t open table. File \"");
		std::string sFileName(nameOfTable);
		std::string sEnd("\" doesn`t exist.");
		std::string errorMessage;
		errorMessage = sStart + sFileName;
		errorMessage = errorMessage + sEnd;
		e.setMessage(errorMessage);
		throw e;
	} else {
		
		char* type;
		char* nameOfColumn;
		type = (char*) malloc(sizeof(char)*100);
		while ( fscanf(stream2,"%s",type)!=EOF)
		{
			vector<char*> col;
			//TODO: We need type in result? If not - need remove type from this method
			nameOfColumn = (char*) malloc (sizeof(char)*100);
			fscanf(stream2,"%s",nameOfColumn);
			col.push_back(nameOfColumn);
			col.push_back(type);
			result.push_back(col);
			type = (char*) malloc(sizeof(char)*100);
		}
		fclose(stream2);
	}
	return result;
}

vector< vector<char*> > datastore2::getColumns(vector<char*> tables) {
	vector< vector<char*> > result;
	vector< vector<char*> > tableVector;

	for(int i = 0; i < tables.size(); i++) {
		tableVector = this -> getColumns(tables.at(i));
		for(int j = 0; j < tableVector.size(); j++) {
			result.push_back(tableVector.at(j));
		}
	}
	this -> displayResults(result);
	return result;
}

void datastore2::showTables(vector<char*> tables)
{
	for(int i=0;i<tables.size(); i++)
	{
		cout<<tables.at(i);
		cout<<"\n";
	}

}
vector<char*> datastore2::getDataFromColumn(char* nameOfColumn, char* nameOfTable)
{
	std::string fileAdress(nameOfTable);
	fileAdress = fileAdress + "\\";
	fileAdress = fileAdress + nameOfColumn;
	if ( (stream = fopen( (char*) fileAdress.data(), "r")) == NULL) {
		dbmsException e;
		std::string sStart("Can`t open column. File \"");
		std::string sFileName(nameOfColumn);
		std::string sEnd("\" doesn`t exist.");
		std::string errorMessage;
		errorMessage = sStart + sFileName;
		errorMessage = errorMessage + sEnd;
		e.setMessage(errorMessage);
		throw e;
	} else {
		
		int idx;
		int MaxId=1;
		vector<char*> data;
		vector <char*>::iterator v1_Iter;
		char* value;
		data.insert(data.begin(),1,nameOfColumn);
		while (fscanf(stream,"%d",&idx)!= EOF)
		{
			//Display data
			value = (char*) malloc(sizeof(char)*100);
			
			fscanf(stream,"%s",value);
			//Insert into data vector
			if (idx > MaxId) MaxId = idx;
			data.resize(MaxId);
			data.insert(data.begin()+idx,1, value);
		}
		fclose(stream);
		
		return data;
	}
}

void datastore2::setDataToColumn(char* nameOfTable, char* nameOfColumn, char* data) {
	FILE* stream;
	std::string fileAdress(nameOfTable);
	fileAdress = fileAdress + "\\";
	fileAdress = fileAdress + nameOfColumn;
	if( (stream = fopen((char*) fileAdress.data(),"a")) == NULL)
	{
		dbmsException e;
		std::string sStart("Can`t write column, because can`t open \"");
		std::string sFileName(nameOfColumn);
		std::string sEnd("\" for write. ");
		std::string errorMessage;
		errorMessage = sStart + sFileName;
		errorMessage = errorMessage + sEnd;
		e.setMessage(errorMessage);
		throw e;		
	}
	else
	{
		//if (data != NULL) {
			int nextId;
			nextId = this->getDataFromColumn(nameOfColumn, nameOfTable).size();
			fprintf(stream,"\n%d %s\n", nextId, data);
		//}
	}
	fclose(stream);

}

void datastore2::refreshDataToColumn(char* nameOfTable, char* nameOfColumn, vector<char*> data) {
	FILE* stream;
	std::string fileAdress(nameOfTable);
	fileAdress = fileAdress + "\\";
	fileAdress = fileAdress + nameOfColumn;
	if( (stream = fopen((char*) fileAdress.data(),"w+")) == NULL)
	{
		dbmsException e;
		std::string sStart("Can`t write column, because can`t open \"");
		std::string sFileName(nameOfColumn);
		std::string sEnd("\" for write. ");
		std::string errorMessage;
		errorMessage = sStart + sFileName;
		errorMessage = errorMessage + sEnd;
		e.setMessage(errorMessage);
		throw e;		
	}
	else
	{	
		int nextId = 1;	
		for(int i = 1; i < data.size(); i++) {
            if (data.at(i) != NULL) {
				fprintf(stream,"\n%d %s\n", nextId, data.at(i));
			}
			nextId++;
		}
		fclose(stream);
	}
}
void datastore2::setDataToTable(char* nameOfTable, vector<char*> result) {
		
		vector< vector<char*> > columnsInfo;
		columnsInfo = this->getColumns(nameOfTable);
		if(columnsInfo.size() != result.size()) {
			dbmsException e;
			e.setMessage("Wrong columns size. Check columns data and try again...");
			throw e;
		}

		//Check for relational model
		vector< vector<char*> > table;
		table = this->getDataFromTable(nameOfTable);
		for(int col = 0; col < table.size(); col++) {
			char* type;
			type = table[col].back();
			table[col].insert(table[col].end()-1, 1, result.at(col));
		}
		
		if(!relationalModel::isTableRelational(table, nameOfTable)) {
			dbmsException e;
			e.setMessage("Wrong data type or data already exist... Check query and try again.");
			throw e;
		}
		
		vector<char*> typeOfColumns;
		vector<char*> nameOfColumns;
		for(int i = 0; i < columnsInfo.size(); i++) {
			nameOfColumns.push_back(columnsInfo[i].at(0));
			typeOfColumns.push_back(columnsInfo[i].at(1));
		}
		//Write columns data to file
		for( int col = 0; col < nameOfColumns.size(); col++) {
			this->setDataToColumn(nameOfTable, nameOfColumns.at(col), result.at(col));
		}
		

}
void datastore2::refreshDataToTable(char* nameOfTable, vector< vector<char*> > result) {
	vector< vector<char*> > columnsInfo;
	columnsInfo = this->getColumns(nameOfTable);
	if(columnsInfo.size() != result.size()) {
		dbmsException e;
		e.setMessage("Wrong columns size. Check columns data and try again...");
		throw e;
	}
	vector<char*> typeOfColumns;
	vector<char*> nameOfColumns;
	for(int i = 0; i < columnsInfo.size(); i++) {
		nameOfColumns.push_back(columnsInfo[i].at(0));
		typeOfColumns.push_back(columnsInfo[i].at(1));
	}
	//Write columns data to file
	for( int col = 0; col < nameOfColumns.size(); col++) {
		this->refreshDataToColumn(nameOfTable, nameOfColumns.at(col), result.at(col));
	}
}

void datastore2::createTable(char* nameOfTable, vector<char*> columns, vector<char*> columnsTypes) {
	for(int col = 0; col < columns.size()-1; col++) {
		for(int j = col + 1; j < columns.size(); j++) {
			if(strcmp(columns.at(col),columns.at(j)) == 0) {
				dbmsException e;
				e.setMessage("Can`t create columns with equals columns name");
				throw e;
			}
		}
	}
	if (CreateDirectory(nameOfTable, NULL)) {
		FILE* stream;
		std::string fileAdress(nameOfTable);
		fileAdress = fileAdress + "\\";
		fileAdress = fileAdress + nameOfTable;
		stream = fopen( (char*) fileAdress.data(), "w+");

		for(int i = 0; i < columns.size(); i++) {
			std::string columnPath(nameOfTable);
			columnPath = columnPath + "\\";
			columnPath = columnPath + columns.at(i);
			fopen( (char*) columnPath.data(), "w+");
			fprintf(stream, "%s ", columnsTypes.at(i));
			fprintf(stream, "%s\n"	, columns.at(i));

		}
		fclose(stream);


	} else if(GetLastError() == ERROR_ALREADY_EXISTS) {
		dbmsException e;
		e.setMessage("Table already exist");
		throw e;
	} else {
		dbmsException e;
		e.setMessage("Unknow error, please try again...");
		throw e;
	}
}

void datastore2::dropTable(char* nameOfTable) {
	
	nameOfTable[strlen(nameOfTable) + 1] = 0;

	int res;
	SHFILEOPSTRUCT fo;
	ZeroMemory(&fo, sizeof(fo));
	fo.pFrom  = nameOfTable;	// pFrom & pTo have to include extra \0 in the end, so \0\0 in the end
	fo.wFunc  = FO_DELETE;
	fo.fFlags = FOF_NOCONFIRMATION | FOF_SILENT;;
	res = SHFileOperation(&fo);
	if(res != 0) {
		dbmsException e;
		e.setMessage("Table doesn`t exist");
		throw e;
	}


}
char* datastore2::getColumnType(char* nameOfTable, char* nameOfColumn){
	
	vector< vector<char*> > columnsInfo;
	columnsInfo = this->getColumns(nameOfTable);
	for(int i = 0; i < columnsInfo.size(); i++) {
		//check name
		if(strcmp(columnsInfo[i].at(0), nameOfColumn) == 0) {
			return columnsInfo[i].at(1);
		}
		
	}
	dbmsException e;
	e.setMessage("dbcoreError: Column doesn`t exist");
	throw e;
}